//
//  Y2Router.m
//  Y2Router
//
//  Created by harry.tong on 2021/6/3.
//

#import "Y2Router.h"
#import <objc/runtime.h>
#import "Y2EventBusProtocol.h"

@interface Y2Router()<NSCopying,NSMutableCopying>

@property (nonatomic, strong) NSMutableDictionary *moduleProtocols;

@property (nonatomic, strong) NSMutableDictionary *moduleInstances;

@end

static Y2Router *instance = nil;

@implementation Y2Router

+ (instancetype)shareManager{
    static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[self alloc] init];

            [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
                            
            }];
        });
        return instance;
}

+ (id)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}

 
- (id)copyWithZone:(nullable NSZone *)zone{
    return instance;
}

- (id)mutableCopyWithZone:(nullable NSZone *)zone{
    return instance;
}

-(NSMutableDictionary *) moduleProtocols{
    if (!_moduleProtocols){
        _moduleProtocols = [[NSMutableDictionary alloc] init];
    }
    return _moduleProtocols;
}

-(NSMutableDictionary *) moduleInstances{
    if (!_moduleInstances){
        _moduleInstances = [[NSMutableDictionary alloc] init];
    }
    return _moduleInstances;
}

-(void) applicationDidFinishLaunching:(NSNotification *)notification{
    [Y2Router setup];
}

+(void) registerForModule:(Class<Y2EventBusProtocol>)cls protocol:(Protocol<Y2EventBusProtocol> *)ptl{
    BOOL isComply = NO;
    unsigned int count;
    // 获取ptl 遵循的协议
    Protocol * __unsafe_unretained *list = protocol_copyProtocolList(ptl, &count);
    for (int i = 0; i < count; i++) {
        Protocol *proto = list[i];
        const char *name = protocol_getName(proto);
        if ([[NSString stringWithUTF8String:name] isEqualToString:NSStringFromProtocol(@protocol(Y2EventBusProtocol))]){
            isComply = YES;
        }
    }
    // ptl支持遵守一个协议，Y2EventBusProtocol
    if (count != 1 && !isComply){
        return;
    }
    
    // cls 是否实现了ptl协议
    if (class_conformsToProtocol(cls, ptl) == NO){
        return;
    }
    [[Y2Router shareManager].moduleProtocols setObject:NSStringFromProtocol(ptl)  forKey:NSStringFromClass(cls)];
}


+(void) setup{
    NSArray *list = [Y2Router shareManager].moduleProtocols.allKeys;
    int channelSDKCount = 0;
    for (NSString *str in list){
        id<Y2EventBusProtocol> module = [[NSClassFromString(str) alloc] init];
        if ([module respondsToSelector:@selector(initInfo)]){
            [module initInfo];
        }
        if ([module respondsToSelector:@selector(moduleType)]){
            
            if ([module moduleType] == ModuleForChannelType){
                channelSDKCount += 1;
            }
            [[Y2Router shareManager].moduleInstances setObject:module forKey:str];
        }
    }
}

+(id _Nullable) callWithProtocol:(NSString * _Nonnull)protocol
                      moduleType:(NSString * _Nonnull)type
                          method:(NSString * _Nonnull)method
                            args:(NSString * _Nullable)args{
    NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
    return [Y2Router handleURL:[NSString stringWithFormat:@"NebulaJoy://%@/sdk/%@/%@/%@?args=%@",
                                ([infoDict objectForKey:@"CFBundleIdentifier"] ?: @""),
                                type,
                                protocol,
                                method,
                                (args ?: @"")]];
}

+(id _Nullable) handleURL:(NSString * _Nonnull)url{
    NSURL *routerURL = [NSURL URLWithString:url];
    NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
    if (![[routerURL host] isEqualToString:[infoDict objectForKey:@"CFBundleIdentifier"]]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    NSArray *paths = [routerURL pathComponents];
    
    // 内部协议跳转
    NSString *protocolPath = [paths objectAtIndex:2];
    if ([protocolPath isEqualToString:NSStringFromProtocol(@protocol(Y2EventBusProtocol))] &&
        [[paths objectAtIndex:1] isEqualToString:NSStringFromClass([self class])]){
        NSString *method = [paths objectAtIndex:3];
        NSString *argsPath = [routerURL query];
        NSString *argsStr = NULL;
        if (argsPath.length > 0){
            argsStr = [[[[NSURLComponents alloc] initWithString:url] queryItems] firstObject].value;
        }
        NSData *jsonData = [argsStr dataUsingEncoding:NSUTF8StringEncoding];
        NSError *err;
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
                                                            options:NSJSONReadingMutableContainers
                                                              error:&err];
        if(err) {
            return nil;
        }
        if ([method isEqualToString:@"StateMachineTransition"]){
            [Y2Router stateMachineTransition:[[dic objectForKey:@"status"] intValue] info:[dic objectForKey:@"info"]];
        }else if ([method isEqualToString:@"OpenWebView"]){
            
        }
        return nil;
    }
    // 该协议是否已注册
    if (![[Y2Router shareManager].moduleInstances.allValues containsObject:protocolPath]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    // 获取协议实现类
    NSString *clsName = [[[Y2Router shareManager].moduleProtocols allKeysForObject:protocolPath] firstObject];
    id<Y2EventBusProtocol> obj = [[Y2Router shareManager].moduleInstances objectForKey:clsName];
    // 是否实现模块类型方法
    if (![obj respondsToSelector:@selector(moduleType)]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    Y2ModuleType type = [obj moduleType];
    NSString *typeStr = @"plugins";
    if (type == ModuleForChannelType){
        typeStr = @"channels";
    }
    // url模块名是否与实例模块类型保持一致
    if (![typeStr isEqualToString:[paths objectAtIndex:1]]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    // 该协议是否已实现
    if (![obj conformsToProtocol:NSProtocolFromString(protocolPath)]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    // 该方法是否已实现
    NSString *methodPath = [paths objectAtIndex:3];
    if (methodPath.length <= 0 || ![obj respondsToSelector:NSSelectorFromString(methodPath)]){
        @throw [[NSException alloc] initWithName:@"" reason:@"" userInfo:nil];
    }
    // 解析参数
    NSString *argsPath = [routerURL query];
    NSString *argsStr = NULL;
    if (argsPath.length > 0){
        argsStr = [[[[NSURLComponents alloc] initWithString:url] queryItems] firstObject].value;
    }
    NSMethodSignature *sign = [NSClassFromString(clsName) instanceMethodSignatureForSelector:NSSelectorFromString(methodPath)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sign];
    if (argsStr.length > 0 && argsStr != NULL){
        [invocation setArgument:(__bridge void * _Nonnull)(argsStr) atIndex:2];
    }
    invocation.target = obj;
    invocation.selector = NSSelectorFromString(methodPath);
    [invocation invoke];
    id result;
    [invocation getReturnValue:&result];
    return result;
    
}

+(void) stateMachineTransition:(int)status info:(NSDictionary *)info{
    NSArray *list = [Y2Router shareManager].moduleInstances.allValues;
    dispatch_apply(list.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        id<Y2EventBusProtocol> obj = [list objectAtIndex:index];
        if ([obj respondsToSelector:@selector(moduleStatusWithChange:info:)]){
            [obj moduleStatusWithChange:status info:info];
        }
    });
}


+(void) applicationLiftCycleMethod:(SEL _Nonnull)sel info:(id<AspectInfo> _Nonnull)info{
    NSMutableArray *list = [NSMutableArray arrayWithArray:[Y2Router shareManager].moduleInstances.allValues];
    [list insertObject:[Y2Router shareManager] atIndex:0];
    dispatch_apply(list.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSObject<UIApplicationDelegate> *obj = [list objectAtIndex:index];
        if ([obj conformsToProtocol:@protocol(UIApplicationDelegate)] && [obj respondsToSelector:sel]){
            NSMethodSignature *signature = [obj methodSignatureForSelector:sel];
            if (signature){
                NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
                invocation.target = obj;
                invocation.selector = sel;
                
                NSUInteger numberOfArguments = signature.numberOfArguments;
                if (numberOfArguments > info.originalInvocation.methodSignature.numberOfArguments) {
                    NSLog(@"lifecycle has too many arguments. Not calling %@", info);
                }else{
                    void *argBuf = NULL;
                    for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {
                        const char *type = [info.originalInvocation.methodSignature getArgumentTypeAtIndex:idx];
                        NSUInteger argSize;
                        NSGetSizeAndAlignment(type, &argSize, NULL);
                        
                        if (!(argBuf = reallocf(argBuf, argSize))) {
                            NSLog(@"Failed to allocate memory for lifecycle invocation.");
                            break;
                        }
                        
                        [info.originalInvocation getArgument:argBuf atIndex:idx];
                        [invocation setArgument:argBuf atIndex:idx];
                    }
                    [invocation invoke];
                    
                    if (argBuf != NULL) {
                        free(argBuf);
                    }
                }
            }
        }
    });
}


+(void) feedBackStatus:(int)status info:(NSDictionary *)info{
    
}
//    BOOL isContainerPrototol = NO;
//    NSString *protocolName = NULL;
//    unsigned int count;
//    Protocol * __unsafe_unretained *protocols = objc_copyProtocolList(&count);
//    for (int i = 0; i < count; i++) {
//        Protocol *pro = protocols[i];
//        if ([NSStringFromProtocol(pro) isEqualToString:@"SuperConnectProtocol"]){
//            protocolName = NSStringFromProtocol(pro);
//            isContainerPrototol = YES;
//        }
//    }
//    free(protocols);
//    if (!isContainerPrototol || protocolName == NULL || protocolName.length == 0){
//        return;
//    }
//    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:info];
//    [dict setObject:[NSString stringWithFormat:@"%d",(NSUInteger)status] forKey:@"action"];
//    NSString *dataStr = @"";
//    if (@available(iOS 5.0, *)) {
//        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:0];
//        dataStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
//    } else {
//        return;
//    }
//    [Y2Router callWithProtocol:protocolName moduleType:@"plugin" method:@"callBack" args:@""];
//}
//
//+(ModuleStatus) currentStatus{
//    return _status;
//}

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions{
    [Y2Router setup];

    return YES;
}

@end

